package com.wrb.appointment.exception;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RestController
@EnableConfigurationProperties({ServerProperties.class})
@Slf4j
public class GlobalExceptionController extends AbstractErrorController {

	@Autowired
	public GlobalExceptionController(ErrorAttributes errorAttributes) {
		super(errorAttributes);
	}

	public GlobalExceptionController(ErrorAttributes errorAttributes, List<ErrorViewResolver> errorViewResolvers) {
		super(errorAttributes, errorViewResolvers);
	}

	@RequestMapping(value = "/error")
	public ResponseEntity error(HttpServletRequest request) {

		//请求的基本信息获取
		Object value = request.getAttribute("javax.servlet.error.status_code");
		int code = value == null ? -1 : (int) value;
		String path = String.valueOf(request.getAttribute("javax.servlet.forward.request_uri"));
		String requestParams = this.parseQueryString(request);
		if (code == 404) {
			return this.error404(path, requestParams);
		}
		String message = code == 500 ? "未知异常" : String.valueOf(request.getAttribute("javax.servlet.error.message"));
		//获取捕获到的异常对象
		value = request.getAttribute("org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR");
		Throwable throwable = value == null ? new ServiceException(-1, "异常捕捉发生异常") : (Throwable) value;

		//判断是否为自定义异常
		if (throwable instanceof ServiceException) {
			ServiceException remoteServiceException = (ServiceException) throwable;
			code = remoteServiceException.getCode();
			message = remoteServiceException.getMessage();
		}
		//异常响应数据组装
		ErrorResponse errorResponse = new ErrorResponse();
		errorResponse.setCode(code);
		errorResponse.setMsg(message);
		errorResponse.setRequestUrl(path);
		errorResponse.setRequestParams(requestParams);
		errorResponse.setData(ExceptionUtils.getRootCauseStackTrace(throwable));
		//输出错误日志
		log.error(String.format("Found a exception,path is %s ,query string is %s,code is %d", path, requestParams, code), throwable);
		//响应异常数据
		return ResponseEntity.ok(errorResponse);
	}

	@Data
	class ErrorResponse {
		private int code;
		private String msg;
		private String requestUrl;
		private String requestParams;
		private Object data;
	}

	private ResponseEntity error404(String path, String requestParams) {
		ErrorResponse errorResponse = new ErrorResponse();
		errorResponse.setCode(HttpStatus.NOT_FOUND.value());
		errorResponse.setMsg("This application has no explicit mapping");
		errorResponse.setRequestUrl(path);
		errorResponse.setRequestParams(requestParams);
		return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
	}

	private String parseQueryString(HttpServletRequest request) {
		Object requestParams = request.getAttribute("requestParams");
		if (requestParams == null) {
			return String.valueOf(request.getAttribute("javax.servlet.forward.query_string"));
		}
		try {
			Map<String, String[]> parameterMap = (Map<String, String[]>) requestParams;
			List<String> paramList = new ArrayList<>();
			for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
				paramList.add(entry.getKey() + "=" + String.join(",", entry.getValue()));
			}
			return String.join("&", paramList);
		} catch (Exception e) {
			e.printStackTrace();
			return "Parse query string is error! -_-||| ";
		}
	}

	@Override
	public String getErrorPath() {
		return null;
	}
}
